Day 4
고급 시각화와
대시보드의 설계

이상일(서울대학교 지리교육과 교수)

2024-08-08

커뮤니케이션을 위한 시각화

https://r4ds.hadley.nz/intro

내용

  • 동적ㆍ반응형 시각화

    • 상호작용성과 역동성이 부가된 시각화
    • 방식
      • 임베딩(embedding)
      • 동적ㆍ반응형 테이블과 그래프 제작
  • 지리공간적 시각화

    • “지도는 텍스트, 테이블, 챠트와 같은 것들 보다 훨씬 더 효과적으로 정보를 전달할 수 있다.”

자바스크립트 라이브러리: 정의

  • JavaScript Library

  • 자바스크립트 프로그래밍 언어를 사용하여 웹 개발을 더 쉽고 효율적으로 할 수 있도록 도와주는 재사용 가능한 코드 모음

  • 동적ㆍ반응형 웹 페이지 제작을 위한 다양한 기능과 도구를 제공

  • HTML의 script 태그를 통해 웹사이트에 막바로 임베드할 수 있음

  • R의 래퍼 패키지를 통해 손쉽게 사용 가능

자바스크립트 라이브러리: 종류

https://data-flair.training/blogs/javascript-libraries/

자바스크립트 라이브러리: 데이터 시각화

https://www.geeksforgeeks.org/javascript-libraries-for-data-visualization/

동적ㆍ반응형 시각화

임베딩 Embedding

  • 동적ㆍ반응형 시각화가 구현되어 있는 웹사이트를 불러와 내재화

  • HTML의 iframe 태그 활용

임베딩 Embedding: 사례 1

<iframe src="https://kosis.kr/edu/share.do?shareID=S0500_16" 
loading="lazy" style="width: 100%; height: 600px; border: 
0px none;" allow="web-share; clipboard-write"></iframe>

임베딩 Embedding: 사례 2

<iframe src="https://ourworldindata.org/grapher/child-mortality?time=earliest..latest&tab=chart" 
loading="lazy" style="width: 100%; height: 600px; border: 0px none;" allow="web-share; clipboard-write"></iframe>

테이블 Tables

  • 테이블 역시 시각화의 일부

    • 데이터 변형 및 요약을 거친 테이블

    • 시각성이 가미된 테이블

    • 인트렉티브 테이블

테이블 Tables

https://r-graph-gallery.com/table.html

테이블 Tables: gt 패키지

https://gt.rstudio.com/

테이블 Tables: gt 패키지

https://gt.rstudio.com/

테이블 Tables: gt 패키지

https://towardsdatascience.com/exploring-the-gt-grammar-of-tables-package-in-r-7fff9d0b40cd

테이블 Tables: DT 패키지

https://datatables.net/

테이블 Tables: DT 패키지

  • DT의 기능

    • Pagination: 페이지를 이동할 수 있는 기능

    • Instant search: 즉각적인 찾기 기능(Search에 타이핑하기 시작하면 즉각적으로 검색 결과 보여줌)

    • Multi-column ordering: 다중 컬럼 정렬 기능(컬럼 하나를 선택한 후 ctrl을 누른 상태에서 다른 컬럼을 선택)

    • Filtering: 값을 정렬할 수 있는 기능

    • Editable: 셀 값을 수정할 수 있는 기능

    • Buttons: 셀 숨기기, CSV, PDF, XLSX 등의 확장자로 내보내기 등을 수행하는 버튼 생성 기능

테이블 Tables

gapminder |> 
  datatable(
    filter = "top",
    extensions = "Buttons",
    options = list(
      pageLength = 5,
      autoWidth = TRUE,
      dom = "Bftip",
      buttons = c("copy", "excel", "pdf", "print")
    )
  )

그래프 Graphs

그래프 Graphs: plotly

https://plotly.com/

그래프 Graphs: plotly

library(plotly)
gapminder |> 
  filter(year == 2007) |> 
  plot_ly(
    x = ~gdpPercap, 
    y = ~lifeExp, 
    color = ~continent,
    text = ~paste(
      "Country: ", country, 
      "<br>GDP per capita: ", gdpPercap, 
      "$<br>Life Expectancy at Birth:", lifeExp
    )
  )

그래프 Graphs: plotly

gapminder |> 
  plot_ly(
    x = ~log10(gdpPercap), y = ~lifeExp,
    text = ~paste(
      "Country:", country, 
      "</br>Continent:", continent, 
      "</br>lifeExp:", lifeExp)
    ) |> 
  add_markers(
    color = ~continent, 
    size = ~pop, 
    frame = ~year, 
    marker = list(sizeref = 0.2, sizemode = "area")
  )

그래프 Graphs: ggplotly()

P <- gapminder |> 
  filter(year == 2007) |> 
  ggplot(aes(x = gdpPercap, y = lifeExp, color = continent)) +
  geom_point() + 
  scale_color_brewer(palette = "Set2") +
  theme_minimal()
ggplotly(P)

그래프 Graphs: gganimate

library(gganimate)
P <- gapminder |> 
  ggplot(aes(x = gdpPercap, y = lifeExp, size = pop, color = continent)) +
  geom_point(show.legend = FALSE, alpha = 0.7) +
  scale_x_log10() +
  scale_size(range = c(2, 12))
P + transition_time(year) +
  labs(title = "Year: {frame_time}")

지리공간적 시각화

개념적 기초

  • 지리공간적 데이터: 형상 데이터 + 속성 데이터

    • 형상 데이터 (기하, 도형, 공간 데이터)

      • 행정구역 경계와 같은 지리공간적 객체 자체에 대한 데이터

      • 버텍스(vertex)의 좌표값을 가진 데이터

      • 포인트(점), 라인(선), 폴리곤(역)의 형상으로 표출

    • 속성 데이터

      • 지리공간적 객체가 보유한 속성

      • 기존 일반 데이터와 동일

  • 조인: left_join() 함수

    • 왼편: 형상 데이터

    • 오른편: 속성 데이터

개념적 기초

  • 셰이프 파일(shape file): 가장 널리 사용되는 형상 데이터

    • sigungu.shp: 버텍스의 좌표값이 포함된 핵심 파일

    • sigungu.shx: 공간적 인덱싱 파일

    • sigungu.dbf: 기본 속성 파일

    • sigungu.prj: 투영 정보 파일

  • 특수한 패키지 필요: sf 패키지

    • st_read() 함수

sf 패키지

https://allisonhorst.com/r-packages-functions

sf 패키지: 주요 함수

  • 읽고 쓰기: st_read(), st_write()

  • 투영법 관련: st_crs(), st_transform()

  • 기하 측정: st_area(), st_length(), st_perimeter(), st_distance()

  • 기하 변형: st_centroid(), st_buffer(), st_boundary(), st_simplify()

  • 기하 생성: st_point(), st_voronoi() , st_convex_hull(), st_make_grid()

  • 기하 검토: st_is_valid(), st_make_valid()

  • 중첩 오퍼레이션: st_intersection(), st_union(), st_crop()

  • 기타: st_coordinates(), st_cast(), st_as_sf(), st_graticule(), st_join()

sf 패키지: st_read()

library(sf)
library(tmap)
sigungu_shp <- st_read("sigungu.shp", options = "ENCODING=CP949")
qtm(sigungu_shp)

ggplot2를 이용한 지도 제작: 세계지도

library(tidyverse)
library(spData)
library(sf)
data(world)
world <- st_as_sf(world)
wpp_2022 <- read_rds("wpp_2022.rds")
my_wpp <- wpp_2022 |> 
  filter(year == 2024)
world_data <- world |>
  left_join(my_wpp, join_by(iso_a2 == ISO2))
world_map <- ggplot() +
  geom_sf(data = world_data, aes(fill = TFR, text = name_long)) +
  coord_sf(crs = "+proj=robin") +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(-180, 180, 30)) +
  scale_y_continuous(breaks = c(-89.5, seq(-60, 60, 30), 89.5)) +
  theme(
    panel.background = element_rect("white"),
    panel.grid = element_line(color = "gray80")
  )
world_map

ggplot2를 이용한 지도 제작: 우리나라 지도

library(tidyverse)
library(sf)
sido_shp <- st_read("sido.shp", options = "ENCODING=CP949")
sigungu_shp <- st_read("sigungu.shp", options = "ENCODING=CP949")
data_sigungu <- read_rds("data_sigungu.rds")
sigungu_data <- sigungu_shp |> 
  left_join(
    data_sigungu, join_by(SGG1_CD == C1)
  )
sido_shp <- st_read("sido.shp", options = "ENCODING=CP949")
sigungu_data <- sigungu_data |> 
  mutate(
    index_class = case_when(
      index < 0.2 ~ "1",
      index >= 0.2 & index < 0.5 ~ "2",
      index >= 0.5 & index < 1.0 ~ "3",
      index >= 1.0 & index < 1.5 ~ "4",
      index >= 1.5 ~ "5"
    ),
    index_class = fct(index_class, levels = as.character(1:5))
  )
class_color <- c("1" = "#d7191c", "2" = "#fdae61",
                 "3" = "#ffffbf", "4" = "#a6d96a", 
                 "5" = "#1a9641")
ggplot_map <- ggplot() +
  geom_sf(
    data = sigungu_data, 
    aes(fill = index_class, text = SGG1_FNM), 
    show.legend = TRUE
  ) +
  geom_sf(
    data = sido_shp, 
    fill = NA, 
    lwd = 0.5
  ) +
  scale_fill_manual(
    name = "Classes", 
    labels = c("< 0.2", "0.2 ~ 0.5", "0.5 ~ 1.0", 
               "1.0 ~ 1.5", ">= 1.5"), 
    values = class_color, drop = FALSE
  ) 
ggplot_map

인터랙티브 지도: 세계지도

ggplotly(world_map)

인터렉티브 지도: 우리나라 지도

library(ggiraph)
sigungu_data <- sigungu_data |> 
  mutate(
    index = format(index, digits = 4, nsmall = 4),
    my_tooltip = str_c("Name: ", SGG1_FNM, "\n Index: ", index)
  )
gg <- ggplot() +
  geom_sf_interactive(
    data = sigungu_data, 
    aes(
      fill = index_class, 
      tooltip = my_tooltip, 
      data_id = SGG1_FNM
    ), 
    show.legend = TRUE
  ) +
  geom_sf(
    data = sido_shp, 
    fill = NA, 
    lwd = 0.5
  ) +
  scale_fill_manual(
    name = "Classes", 
    labels = c("< 0.2", "0.2 ~ 0.5", "0.5 ~ 1.0", 
               "1.0 ~ 1.5", ">= 1.5"), 
    values = class_color, drop = FALSE
  ) 
girafe(ggobj = gg) |> 
  girafe_options(
    opts_hover(css = "fill: gray")
  )

leaflet: 자바스크립트 라이브러리

https://leafletjs.com/

leaflet: 단순 일반도

library(leaflet)
leaflet() |> 
  addTiles() |> 
  addPopups(126.955184, 37.460422, "Sang-Il's Office",
            options = popupOptions(closeButton = FALSE))

leaflet: 매시업(mashup) 지도